package mergecluster

import (
	"context"
	"testing"
	"time"

	confv1beta1 "gopkg.openfuyao.cn/bkecommon/cluster/api/v1beta1"
	bkeinit "gopkg.openfuyao.cn/bkecommon/cluster/initialize"
	"gotest.tools/v3/assert"
	corev1 "k8s.io/api/core/v1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/json"
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
	clientgoscheme "k8s.io/client-go/kubernetes/scheme"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/client/fake"

	bkev1beta1 "gopkg.openfuyao.cn/cluster-api-provider-bke/api/v1beta1"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/pkg/phaseframe/phaseutil"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils/annotation"
	"gopkg.openfuyao.cn/cluster-api-provider-bke/utils/condition"
)

// todo 修复测试
func TestCombinedBKEClusterUpdate(t *testing.T) {
	t.Run("TestUpdateCombinedBKEClusterNodes", TestUpdateCombinedBKEClusterNodes)
	t.Run("TestUpdateCombinedBKEClusterNormalFields", TestUpdateCombinedBKEClusterNormalFields)
	t.Run("TestUpdateSetAnnotaons", TestUpdateSetAnnotaons)
}

type fakeClient struct {
	startTime time.Time
	client.Client
}

type beforeObjNodes struct {
	// 初始configMap
	cmSpecNodes   []confv1beta1.Node
	cmStatusNodes []confv1beta1.Node

	// 初始bkeCluster
	clusterSpecNodes   []confv1beta1.Node
	clusterStatusNodes []confv1beta1.Node

	// 初始聚合bkeCluster
	cClusterSpecNodes   []confv1beta1.Node
	cClusterStatusNodes []confv1beta1.Node
}

type afterObjNodes struct {
	// 更新后configMap
	cmSpecNodes   []confv1beta1.Node
	cmStatusNodes []confv1beta1.Node

	// 更新后bkeCluster
	clusterSpecNodes   []confv1beta1.Node
	clusterStatusNodes []confv1beta1.Node
}

const (
	globalNameSpace = "default"

	fakeClusterName = "bc-t"

	password = "123456"
	port     = "22"
	username = "root"
)

var (
	bkeClusterObjMeta = metav1.ObjectMeta{
		Namespace:       globalNameSpace,
		Name:            fakeClusterName,
		ResourceVersion: "1",
	}
	bkeClusterGetKey = client.ObjectKey{
		Namespace: globalNameSpace,
		Name:      fakeClusterName,
	}

	ctx = context.Background()

	fakeConfigMapBefore  *corev1.ConfigMap
	fakeBkeClusterBefore *bkev1beta1.BKECluster
	combinedBKECluster   *bkev1beta1.BKECluster

	masterRole = []string{"master", "etcd"}
	workerRole = []string{"worker"}

	node1 = confv1beta1.Node{
		Hostname: "node1",
		IP:       "1.1.1.1",
		Role:     masterRole,
		Password: password,
		Port:     port,
		Username: username,
	}
	node2 = confv1beta1.Node{
		Hostname: "node2",
		IP:       "2.2.2.2",
		Role:     masterRole,
		Password: password,
		Port:     port,
		Username: username,
	}
	node3 = confv1beta1.Node{
		Hostname: "node3",
		IP:       "3.3.3.3",
		Role:     masterRole,
		Password: password,
		Port:     port,
		Username: username,
	}
	node4 = confv1beta1.Node{
		Hostname: "node4",
		IP:       "4.4.4.4",
		Role:     workerRole,
		Password: password,
		Port:     port,
		Username: username,
	}
)

func newFakeClient() *fakeClient {
	scheme := runtime.NewScheme()
	utilruntime.Must(bkev1beta1.AddToScheme(scheme))
	utilruntime.Must(clientgoscheme.AddToScheme(scheme))

	return &fakeClient{
		startTime: time.Now(),
		Client: fake.NewClientBuilder().
			WithScheme(scheme).
			WithObjects(fakeConfigMapBefore, fakeBkeClusterBefore).
			Build(),
	}
}

func newBeforeObj(objNodes beforeObjNodes) {
	fakeConfigMapBeforeUpdate(objNodes.cmSpecNodes, objNodes.cmStatusNodes)
	fakeBkeClusterBeforeUpdate(objNodes.clusterSpecNodes, objNodes.clusterStatusNodes)
	//combinedBKEClusterBeforeUpdate(objNodes.cClusterSpecNodes, objNodes.cClusterStatusNodes)
}

// TestUpdateCombinedBKEClusterNodes 测试更新聚合bkeCluster的节点
func TestUpdateCombinedBKEClusterNodes(t *testing.T) {
	//该测试模拟了一个部署成功的集群，通过configMap又成功加入了一个节点 2.2.2.2
	//初始configMap              	spec 2.2.2.2           		   status
	//初始bkeCluster             	spec 1.1.1.1 3.3.3.3           status 1.1.1.1 3.3.3.3
	//初始聚合bkeCluster          	spec 1.1.1.1 3.3.3.3 2.2.2.2   status 1.1.1.1 3.3.3.3
	//操作后聚合bkeCluster        	spec 1.1.1.1 3.3.3.3 2.2.2.2   status 1.1.1.1 3.3.3.3 2.2.2.2
	//更新后configMap             	spec 2.2.2.2           		   status 2.2.2.2
	//更新后bkeCluster            	spec 1.1.1.1 3.3.3.3           status 1.1.1.1 3.3.3.3
	t.Run("add node to configMap when cluster ready", TestAddNodeToConfigMapReady)

	//该测试模拟了一个部署成功的集群，通过bkeCluster又成功加入了一个节点 3.3.3.3
	//初始configMap               	spec 2.2.2.2           			status 2.2.2.2
	//初始bkeCluster              	spec 1.1.1.1 3.3.3.3   			status 1.1.1.1
	//初始聚合bkeCluster           	spec 1.1.1.1 2.2.2.2 3.3.3.3    status 1.1.1.1 2.2.2.2
	//操作后聚合bkeCluster         	spec 1.1.1.1 2.2.2.2 3.3.3.3	status 1.1.1.1 2.2.2.2 3.3.3.3
	//更新后configMap              	spec 2.2.2.2           			status 2.2.2.2
	//更新后bkeCluster             	spec 1.1.1.1 3.3.3.3   			status 1.1.1.1 3.3.3.3
	t.Run("add node to configMap when cluster ready", TestAddNodeToBkeClusterReady)

	//该测试模拟了一个部署成功的集群，通过configMap又成功删除了一个节点 2.2.2.2
	//初始configMap               	spec                   status 2.2.2.2
	//初始bkeCluster              	spec 1.1.1.1           status 1.1.1.1
	//初始聚合bkeCluster           	spec 1.1.1.1           status 1.1.1.1 2.2.2.2
	//操作后聚合bkeCluster         	spec 1.1.1.1           status 1.1.1.1
	//更新后configMap              	spec                   status
	//更新后bkeCluster             	spec 1.1.1.1           status 1.1.1.1
	t.Run("delete node to configMap when cluster ready", TestDeleteNodeTOConfigMapReady)

	//该测试模拟了一个部署成功的集群，通过bkeCluster又成功删除了一个节点 1.1.1.1
	//初始configMap               	spec 2.2.2.2           status 2.2.2.2
	//初始bkeCluster              	spec                   status 1.1.1.1
	//初始聚合bkeCluster           	spec 2.2.2.2           status 1.1.1.1 2.2.2.2
	//操作后聚合bkeCluster         	spec 2.2.2.2           status 2.2.2.2
	//更新后configMap              	spec 2.2.2.2           status 2.2.2.2
	//更新后bkeCluster             	spec                   status
	t.Run("delete node to bkeCluster when cluster ready", TestDeleteNodeToBkeClusterReady)

	//该测试模拟分别通过configMap和bkeCluster添加了三个节点部署集群 1.1.1.1 2.2.2.2 3.3.3.3
	//初始configMap               	spec 2.2.2.2 3.3.3.3   		   status
	//初始bkeCluster              	spec 1.1.1.1           	 	   status
	//初始聚合bkeCluster           	spec 1.1.1.1 2.2.2.2 3.3.3.3   status
	//操作后聚合bkeCluster         	spec 1.1.1.1 2.2.2.2 3.3.3.3   status 1.1.1.1 2.2.2.2 3.3.3.3
	//更新后configMap              	spec 2.2.2.2 3.3.3.3   		   status 2.2.2.2 3.3.3.3
	//更新后bkeCluster             	spec 1.1.1.1           		   status 1.1.1.1
	t.Run("add node to configMap and bkeCluster deploy cluster", TestAddNodeToConfigMapAndBkeClusterDeploy)

	//该测试模拟分别通过bkeCluster部署了一个三节点集群 1.1.1.1 2.2.2.2 3.3.3.3
	//初始configMap               	spec                   			status
	//初始bkeCluster              	spec 1.1.1.1 2.2.2.2 3.3.3.3    status
	//初始聚合bkeCluster           	spec 1.1.1.1 2.2.2.2 3.3.3.3   	status
	//操作后聚合bkeCluster         	spec 1.1.1.1 2.2.2.2 3.3.3.3   	status 1.1.1.1 2.2.2.2 3.3.3.3
	//更新后configMap              	spec 					        status
	//更新后bkeCluster             	spec 1.1.1.1 2.2.2.2 3.3.3.3    status 1.1.1.1 2.2.2.2 3.3.3.3
	t.Run("add node to bkeCluster deploy cluster", TestAddNodeToBkeClusterDeploy)

	//该测试模拟分别通过configMap和bkeCluster添加了一个节点部署集群，但部署失败
	//初始configMap               	spec 4.4.4.4           status
	//初始bkeCluster              	spec 1.1.1.1           status
	//初始聚合bkeCluster           	spec 1.1.1.1 4.4.4.4   status
	//操作后聚合bkeCluster         	spec 1.1.1.1 4.4.4.4   status
	//更新后configMap              	spec 4.4.4.4           status
	//更新后bkeCluster             	spec 1.1.1.1           status
	t.Run("add node to configMap and bkeCluster deploy cluster failed", TestAddNodeToConfigMapAndBkeClusterDeployFailed)

	//该测试模拟集群通过bkeCluster和configMap部署了一个四节点集群，但此时集群人为删除了两个节点，需要从configMap和bkeCluster的Spec中删除该节点 1.1.1.1 4.4.4.4
	//初始configMap               	spec 4.4.4.4           				   status 4.4.4.4
	//初始bkeCluster              	spec 1.1.1.1 2.2.2.2 3.3.3.3           status 1.1.1.1 2.2.2.2 3.3.3.3
	//初始聚合bkeCluster           	spec 1.1.1.1 2.2.2.2 3.3.3.3 4.4.4.4   status 1.1.1.1 2.2.2.2 3.3.3.3 4.4.4.4
	//操作后聚合bkeCluster         	spec 1.1.1.1 2.2.2.2 3.3.3.3 4.4.4.4   status 1.1.1.1 2.2.2.2 3.3.3.3 4.4.4.4
	//更新后configMap              	spec  						           status 4.4.4.4
	//更新后bkeCluster             	spec 2.2.2.2 3.3.3.3                   status 1.1.1.1 2.2.2.2 3.3.3.3
	t.Run("delete node to configMap and bkeCluster deploy cluster", TestDeleteNodeToConfigMapAndBkeClusterDeploy)
}

// 该测试模拟了一个部署成功的集群，通过configMap又成功加入了一个节点 2.2.2.2
func TestAddNodeToConfigMapReady(t *testing.T) {
	//初始configMap              	spec 2.2.2.2           		   status
	//初始bkeCluster             	spec 1.1.1.1 3.3.3.3           status 1.1.1.1 3.3.3.3
	//初始聚合bkeCluster          	spec 1.1.1.1 3.3.3.3 2.2.2.2   status 1.1.1.1 3.3.3.3
	//操作后聚合bkeCluster        	spec 1.1.1.1 3.3.3.3 2.2.2.2   status 1.1.1.1 3.3.3.3 2.2.2.2
	//更新后configMap             	spec 2.2.2.2           		   status 2.2.2.2
	//更新后bkeCluster            	spec 1.1.1.1 3.3.3.3           status 1.1.1.1 3.3.3.3
	bObjNodes := beforeObjNodes{
		cmSpecNodes:        []confv1beta1.Node{node2},
		cmStatusNodes:      nil,
		clusterSpecNodes:   []confv1beta1.Node{node1, node3},
		clusterStatusNodes: []confv1beta1.Node{node1, node3},

		cClusterSpecNodes:   []confv1beta1.Node{node1, node3, node2},
		cClusterStatusNodes: []confv1beta1.Node{node1, node3},
	}
	aObjNodes := afterObjNodes{
		cmSpecNodes:        []confv1beta1.Node{node2},
		cmStatusNodes:      []confv1beta1.Node{node2},
		clusterSpecNodes:   []confv1beta1.Node{node1, node3},
		clusterStatusNodes: []confv1beta1.Node{node1, node3},
	}
	operateNode := node2

	newBeforeObj(bObjNodes)
	var err error
	fClient := newFakeClient()
	combinedBKECluster, err = GetCombinedBKECluster(ctx, fClient, globalNameSpace, fakeClusterName)
	if err != nil {
		t.Error(err)
		return
	}
	assert.DeepEqual(t, combinedBKECluster.Spec.ClusterConfig.Nodes, bObjNodes.cClusterSpecNodes)
	statusNodes := phaseutil.GetBKENodesFromNodesStatus(combinedBKECluster.Status.NodesStatus)
	assert.DeepEqual(t, statusNodes, bObjNodes.cClusterStatusNodes)

	// 模拟加入了一个新节点
	combinedBKECluster.Status.NodesStatus = append(combinedBKECluster.Status.NodesStatus, confv1beta1.NodeState{Node: operateNode, State: bkev1beta1.NodeUnknown})

	err = UpdateCombinedBKECluster(ctx, fClient, combinedBKECluster, nil)
	if err != nil {
		return
	}
	if err = equal(t, fClient, aObjNodes); err != nil {
		t.Error(err)
	}
}

// 该测试模拟了一个部署成功的集群，通过bkeCluster又成功加入了一个节点 3.3.3.3
func TestAddNodeToBkeClusterReady(t *testing.T) {
	//初始configMap               	spec 2.2.2.2           			status 2.2.2.2
	//初始bkeCluster              	spec 1.1.1.1 3.3.3.3   			status 1.1.1.1
	//初始聚合bkeCluster           	spec 1.1.1.1 2.2.2.2 3.3.3.3    status 1.1.1.1 2.2.2.2
	//操作后聚合bkeCluster         	spec 1.1.1.1 2.2.2.2 3.3.3.3	status 1.1.1.1 2.2.2.2 3.3.3.3
	//更新后configMap              	spec 2.2.2.2           			status 2.2.2.2
	//更新后bkeCluster             	spec 1.1.1.1 3.3.3.3   			status 1.1.1.1 3.3.3.3
	bObjNodes := beforeObjNodes{
		cmSpecNodes:        []confv1beta1.Node{node2},
		cmStatusNodes:      []confv1beta1.Node{node2},
		clusterSpecNodes:   []confv1beta1.Node{node1, node3},
		clusterStatusNodes: []confv1beta1.Node{node1},

		cClusterSpecNodes:   []confv1beta1.Node{node1, node3, node2},
		cClusterStatusNodes: []confv1beta1.Node{node1, node2},
	}
	aObjNodes := afterObjNodes{
		cmSpecNodes:        []confv1beta1.Node{node2},
		cmStatusNodes:      []confv1beta1.Node{node2},
		clusterSpecNodes:   []confv1beta1.Node{node1, node3},
		clusterStatusNodes: []confv1beta1.Node{node1, node3},
	}
	operateNode := node3

	newBeforeObj(bObjNodes)
	var err error
	fClient := newFakeClient()
	combinedBKECluster, err = GetCombinedBKECluster(ctx, fClient, globalNameSpace, fakeClusterName)
	if err != nil {
		t.Error(err)
		return
	}
	assert.DeepEqual(t, combinedBKECluster.Spec.ClusterConfig.Nodes, bObjNodes.cClusterSpecNodes)
	statusNodes := phaseutil.GetBKENodesFromNodesStatus(combinedBKECluster.Status.NodesStatus)
	assert.DeepEqual(t, statusNodes, bObjNodes.cClusterStatusNodes)

	// 模拟加入了一个新节点
	combinedBKECluster.Status.NodesStatus = append(combinedBKECluster.Status.NodesStatus, confv1beta1.NodeState{Node: operateNode, State: bkev1beta1.NodeUnknown})

	err = UpdateCombinedBKECluster(ctx, fClient, combinedBKECluster, nil)
	if err != nil {
		return
	}
	if err = equal(t, fClient, aObjNodes); err != nil {
		t.Error(err)
	}
}

// 该测试模拟了一个部署成功的集群，通过configMap又成功删除了一个节点 2.2.2.2
func TestDeleteNodeTOConfigMapReady(t *testing.T) {
	//初始configMap               	spec                   status 2.2.2.2
	//初始bkeCluster              	spec 1.1.1.1           status 1.1.1.1
	//初始聚合bkeCluster           	spec 1.1.1.1           status 1.1.1.1 2.2.2.2
	//操作后聚合bkeCluster         	spec 1.1.1.1           status 1.1.1.1
	//更新后configMap              	spec                   status
	//更新后bkeCluster             	spec 1.1.1.1           status 1.1.1.1
	bObjNodes := beforeObjNodes{
		cmSpecNodes:   nil,
		cmStatusNodes: []confv1beta1.Node{node2},

		clusterSpecNodes:   []confv1beta1.Node{node1},
		clusterStatusNodes: []confv1beta1.Node{node1},

		cClusterSpecNodes:   []confv1beta1.Node{node1},
		cClusterStatusNodes: []confv1beta1.Node{node1, node2},
	}
	aObjNodes := afterObjNodes{
		cmSpecNodes:   nil,
		cmStatusNodes: nil,

		clusterSpecNodes:   []confv1beta1.Node{node1},
		clusterStatusNodes: []confv1beta1.Node{node1},
	}
	operateNode := node2

	newBeforeObj(bObjNodes)
	var err error
	fClient := newFakeClient()
	combinedBKECluster, err = GetCombinedBKECluster(ctx, fClient, globalNameSpace, fakeClusterName)
	if err != nil {
		t.Error(err)
		return
	}
	assert.DeepEqual(t, combinedBKECluster.Spec.ClusterConfig.Nodes, bObjNodes.cClusterSpecNodes)
	statusNodes := phaseutil.GetBKENodesFromNodesStatus(combinedBKECluster.Status.NodesStatus)
	assert.DeepEqual(t, statusNodes, bObjNodes.cClusterStatusNodes)

	// 模拟删除了一个新节点
	combinedBKECluster.RemoveNodeState(operateNode.IP)

	err = UpdateCombinedBKECluster(ctx, fClient, combinedBKECluster, nil)
	if err != nil {
		return
	}
	if err = equal(t, fClient, aObjNodes); err != nil {
		t.Error(err)
	}
}

// 该测试模拟了一个部署成功的集群，通过bkeCluster又成功删除了一个节点 1.1.1.1
func TestDeleteNodeToBkeClusterReady(t *testing.T) {
	//初始configMap               	spec 2.2.2.2           status 2.2.2.2
	//初始bkeCluster              	spec                   status 1.1.1.1
	//初始聚合bkeCluster           	spec 2.2.2.2           status 1.1.1.1 2.2.2.2
	//操作后聚合bkeCluster         	spec 2.2.2.2           status 2.2.2.2
	//更新后configMap              	spec 2.2.2.2           status 2.2.2.2
	//更新后bkeCluster             	spec                   status
	bObjNodes := beforeObjNodes{
		cmSpecNodes:   []confv1beta1.Node{node2},
		cmStatusNodes: []confv1beta1.Node{node2},

		clusterSpecNodes:   nil,
		clusterStatusNodes: []confv1beta1.Node{node1},

		cClusterSpecNodes:   []confv1beta1.Node{node2},
		cClusterStatusNodes: []confv1beta1.Node{node1, node2},
	}
	aObjNodes := afterObjNodes{
		cmSpecNodes:   []confv1beta1.Node{node2},
		cmStatusNodes: []confv1beta1.Node{node2},

		clusterSpecNodes:   nil,
		clusterStatusNodes: nil,
	}
	operateNode := node1

	newBeforeObj(bObjNodes)
	var err error
	fClient := newFakeClient()
	combinedBKECluster, err = GetCombinedBKECluster(ctx, fClient, globalNameSpace, fakeClusterName)
	if err != nil {
		t.Error(err)
		return
	}
	assert.DeepEqual(t, combinedBKECluster.Spec.ClusterConfig.Nodes, bObjNodes.cClusterSpecNodes)
	statusNodes := phaseutil.GetBKENodesFromNodesStatus(combinedBKECluster.Status.NodesStatus)
	assert.DeepEqual(t, statusNodes, bObjNodes.cClusterStatusNodes)

	// 模拟删除了一个新节点
	combinedBKECluster.RemoveNodeState(operateNode.IP)

	err = UpdateCombinedBKECluster(ctx, fClient, combinedBKECluster, nil)
	if err != nil {
		return
	}
	if err = equal(t, fClient, aObjNodes); err != nil {
		t.Error(err)
	}
}

// 该测试模拟分别通过configMap和bkeCluster添加了三个节点部署集群 1.1.1.1 2.2.2.2 3.3.3.3
func TestAddNodeToConfigMapAndBkeClusterDeploy(t *testing.T) {
	//初始configMap               	spec 2.2.2.2 3.3.3.3   		   status
	//初始bkeCluster              	spec 1.1.1.1           	 	   status
	//初始聚合bkeCluster           	spec 1.1.1.1 2.2.2.2 3.3.3.3   status
	//操作后聚合bkeCluster         	spec 1.1.1.1 2.2.2.2 3.3.3.3   status 1.1.1.1 2.2.2.2 3.3.3.3
	//更新后configMap              	spec 2.2.2.2 3.3.3.3   		   status 2.2.2.2 3.3.3.3
	//更新后bkeCluster             	spec 1.1.1.1           		   status 1.1.1.1
	bObjNodes := beforeObjNodes{
		cmSpecNodes:   []confv1beta1.Node{node2, node3},
		cmStatusNodes: nil,

		clusterSpecNodes:   []confv1beta1.Node{node1},
		clusterStatusNodes: nil,

		cClusterSpecNodes:   []confv1beta1.Node{node1, node2, node3},
		cClusterStatusNodes: nil,
	}
	aObjNodes := afterObjNodes{
		cmSpecNodes:   []confv1beta1.Node{node2, node3},
		cmStatusNodes: []confv1beta1.Node{node2, node3},

		clusterSpecNodes:   []confv1beta1.Node{node1},
		clusterStatusNodes: []confv1beta1.Node{node1},
	}
	operateNodes := []confv1beta1.Node{node1, node2, node3}

	newBeforeObj(bObjNodes)
	var err error
	fClient := newFakeClient()
	combinedBKECluster, err = GetCombinedBKECluster(ctx, fClient, globalNameSpace, fakeClusterName)
	if err != nil {
		t.Error(err)
		return
	}
	assert.DeepEqual(t, combinedBKECluster.Spec.ClusterConfig.Nodes, bObjNodes.cClusterSpecNodes)
	statusNodes := phaseutil.GetBKENodesFromNodesStatus(combinedBKECluster.Status.NodesStatus)
	assert.DeepEqual(t, statusNodes, bObjNodes.cClusterStatusNodes)

	// 模拟加入了两个新节点
	for _, node := range operateNodes {
		combinedBKECluster.Status.NodesStatus = append(combinedBKECluster.Status.NodesStatus, confv1beta1.NodeState{Node: node, State: bkev1beta1.NodeUnknown})
	}

	err = UpdateCombinedBKECluster(ctx, fClient, combinedBKECluster, nil)
	if err != nil {
		return
	}
	if err = equal(t, fClient, aObjNodes); err != nil {
		t.Error(err)
	}
}

// 该测试模拟分别通过bkeCluster部署了一个三节点集群 1.1.1.1 2.2.2.2 3.3.3.3
func TestAddNodeToBkeClusterDeploy(t *testing.T) {
	//初始configMap               	spec                   			status
	//初始bkeCluster              	spec 1.1.1.1 2.2.2.2 3.3.3.3    status
	//初始聚合bkeCluster           	spec 1.1.1.1 2.2.2.2 3.3.3.3   	status
	//操作后聚合bkeCluster         	spec 1.1.1.1 2.2.2.2 3.3.3.3   	status 1.1.1.1 2.2.2.2 3.3.3.3
	//更新后configMap              	spec 					        status
	//更新后bkeCluster             	spec 1.1.1.1 2.2.2.2 3.3.3.3    status 1.1.1.1 2.2.2.2 3.3.3.3
	bObjNodes := beforeObjNodes{
		cmSpecNodes:         nil,
		cmStatusNodes:       nil,
		clusterSpecNodes:    []confv1beta1.Node{node1, node2, node3},
		clusterStatusNodes:  nil,
		cClusterSpecNodes:   []confv1beta1.Node{node1, node2, node3},
		cClusterStatusNodes: nil,
	}
	aObjNodes := afterObjNodes{
		cmSpecNodes:        nil,
		cmStatusNodes:      nil,
		clusterSpecNodes:   []confv1beta1.Node{node1, node2, node3},
		clusterStatusNodes: []confv1beta1.Node{node1, node2, node3},
	}
	operateNodes := []confv1beta1.Node{node1, node2, node3}

	newBeforeObj(bObjNodes)
	var err error
	fClient := newFakeClient()
	combinedBKECluster, err = GetCombinedBKECluster(ctx, fClient, globalNameSpace, fakeClusterName)
	if err != nil {
		t.Error(err)
		return
	}
	assert.DeepEqual(t, combinedBKECluster.Spec.ClusterConfig.Nodes, bObjNodes.cClusterSpecNodes)
	statusNodes := phaseutil.GetBKENodesFromNodesStatus(combinedBKECluster.Status.NodesStatus)
	assert.DeepEqual(t, statusNodes, bObjNodes.cClusterStatusNodes)

	// 模拟加入了三个新节点
	for _, node := range operateNodes {
		combinedBKECluster.Status.NodesStatus = append(combinedBKECluster.Status.NodesStatus, confv1beta1.NodeState{Node: node, State: bkev1beta1.NodeUnknown})
	}

	err = UpdateCombinedBKECluster(ctx, fClient, combinedBKECluster, nil)
	if err != nil {
		return
	}
	if err = equal(t, fClient, aObjNodes); err != nil {
		t.Error(err)
	}
}

// 该测试模拟分别通过configMap和bkeCluster添加了一个节点部署集群，但部署失败
func TestAddNodeToConfigMapAndBkeClusterDeployFailed(t *testing.T) {
	//初始configMap               	spec 4.4.4.4           status
	//初始bkeCluster              	spec 1.1.1.1           status
	//初始聚合bkeCluster           	spec 1.1.1.1 4.4.4.4   status
	//操作后聚合bkeCluster         	spec 1.1.1.1 4.4.4.4   status
	//更新后configMap              	spec 4.4.4.4           status
	//更新后bkeCluster             	spec 1.1.1.1           status
	bObjNodes := beforeObjNodes{
		cmSpecNodes:   []confv1beta1.Node{node4},
		cmStatusNodes: nil,

		clusterSpecNodes:   []confv1beta1.Node{node1},
		clusterStatusNodes: nil,

		cClusterSpecNodes:   []confv1beta1.Node{node1, node4},
		cClusterStatusNodes: nil,
	}
	aObjNodes := afterObjNodes{
		cmSpecNodes:   []confv1beta1.Node{node4},
		cmStatusNodes: nil,

		clusterSpecNodes:   []confv1beta1.Node{node1},
		clusterStatusNodes: nil,
	}

	newBeforeObj(bObjNodes)
	var err error
	fClient := newFakeClient()
	combinedBKECluster, err = GetCombinedBKECluster(ctx, fClient, globalNameSpace, fakeClusterName)
	if err != nil {
		t.Error(err)
		return
	}
	assert.DeepEqual(t, combinedBKECluster.Spec.ClusterConfig.Nodes, bObjNodes.cClusterSpecNodes)
	statusNodes := phaseutil.GetBKENodesFromNodesStatus(combinedBKECluster.Status.NodesStatus)
	assert.DeepEqual(t, statusNodes, bObjNodes.cClusterStatusNodes)

	// 无操作，模拟部署失败

	err = UpdateCombinedBKECluster(ctx, fClient, combinedBKECluster, nil)
	if err != nil {
		return
	}
	if err = equal(t, fClient, aObjNodes); err != nil {
		t.Error(err)
	}
}

// 该测试模拟集群通过bkeCluster和configMap部署了一个四节点集群，但此时集群人为删除了两个节点，
// 需要从configMap和bkeCluster的Spec中删除该节点 1.1.1.1 4.4.4.4, 触发集群scale
func TestDeleteNodeToConfigMapAndBkeClusterDeploy(t *testing.T) {
	//初始configMap               	spec 4.4.4.4           				   status 4.4.4.4
	//初始bkeCluster              	spec 1.1.1.1 2.2.2.2 3.3.3.3           status 1.1.1.1 2.2.2.2 3.3.3.3
	//初始聚合bkeCluster           	spec 1.1.1.1 2.2.2.2 3.3.3.3 4.4.4.4   status 1.1.1.1 2.2.2.2 3.3.3.3 4.4.4.4
	//操作后聚合bkeCluster         	spec 1.1.1.1 2.2.2.2 3.3.3.3 4.4.4.4   status 1.1.1.1 2.2.2.2 3.3.3.3 4.4.4.4
	//更新后configMap              	spec  						           status 4.4.4.4
	//更新后bkeCluster             	spec 2.2.2.2 3.3.3.3                   status 1.1.1.1 2.2.2.2 3.3.3.3
	bObjNodes := beforeObjNodes{
		cmSpecNodes:   []confv1beta1.Node{node4},
		cmStatusNodes: []confv1beta1.Node{node4},

		clusterSpecNodes:   []confv1beta1.Node{node1, node2, node3},
		clusterStatusNodes: []confv1beta1.Node{node1, node2, node3},

		cClusterSpecNodes:   []confv1beta1.Node{node1, node2, node3, node4},
		cClusterStatusNodes: []confv1beta1.Node{node1, node2, node3, node4},
	}
	aObjNodes := afterObjNodes{
		cmSpecNodes:   nil,
		cmStatusNodes: []confv1beta1.Node{node4},

		clusterSpecNodes:   []confv1beta1.Node{node2, node3},
		clusterStatusNodes: []confv1beta1.Node{node1, node2, node3},
	}
	operateNodes := []string{"4.4.4.4", "1.1.1.1"}

	newBeforeObj(bObjNodes)
	var err error
	fClient := newFakeClient()
	combinedBKECluster, err = GetCombinedBKECluster(ctx, fClient, globalNameSpace, fakeClusterName)
	if err != nil {
		t.Error(err)
		return
	}
	assert.DeepEqual(t, combinedBKECluster.Spec.ClusterConfig.Nodes, bObjNodes.cClusterSpecNodes)
	statusNodes := phaseutil.GetBKENodesFromNodesStatus(combinedBKECluster.Status.NodesStatus)
	assert.DeepEqual(t, statusNodes, bObjNodes.cClusterStatusNodes)

	err = UpdateCombinedBKECluster(ctx, fClient, combinedBKECluster, operateNodes)
	if err != nil {
		return
	}
	if err = equal(t, fClient, aObjNodes); err != nil {
		t.Error(err)
	}
}

func nodesToString(nodes []confv1beta1.Node) string {
	if nodes == nil || len(nodes) == 0 {
		return "[]"
	}
	marshal, err := json.Marshal(nodes)
	if err != nil {
		return "[]"
	}
	return string(marshal)

}

func fakeConfigMapBeforeUpdate(specNodes, statusNodes []confv1beta1.Node) {
	specNodeStr := nodesToString(specNodes)
	statusNodeStr := nodesToString(statusNodes)

	fakeConfigMapBefore = &corev1.ConfigMap{
		ObjectMeta: bkeClusterObjMeta,
		Data: map[string]string{
			"nodes":  specNodeStr,
			"status": statusNodeStr,
		},
	}
}

func fakeBkeClusterBeforeUpdate(specNodes, statusNodes []confv1beta1.Node) {
	fakeBkeClusterBefore = &bkev1beta1.BKECluster{
		ObjectMeta: bkeClusterObjMeta,
		Spec: confv1beta1.BKEClusterSpec{
			ClusterConfig: &confv1beta1.BKEConfig{
				Nodes: specNodes,
			},
		},
		//Status: confv1beta1.BKEClusterStatus{
		//	ClusterConfig: &confv1beta1.BKEConfig{
		//		Nodes: statusNodes,
		//	},
		//},
	}
	for _, node := range statusNodes {
		fakeBkeClusterBefore.Status.NodesStatus = append(fakeBkeClusterBefore.Status.NodesStatus, confv1beta1.NodeState{Node: node, State: bkev1beta1.NodeUnknown})
	}
}

func equal(t *testing.T, fClient client.Client, aObjNodes afterObjNodes) error {
	afterBkeCluster := &bkev1beta1.BKECluster{}
	afterConfigMap := &corev1.ConfigMap{}
	if err := fClient.Get(ctx, bkeClusterGetKey, afterBkeCluster); err != nil {
		return err
	}
	if err := fClient.Get(ctx, bkeClusterGetKey, afterConfigMap); err != nil {
		return err
	}

	assert.DeepEqual(t, afterBkeCluster.Spec.ClusterConfig.Nodes, aObjNodes.clusterSpecNodes)
	statusNodes := phaseutil.GetBKENodesFromNodesStatus(combinedBKECluster.Status.NodesStatus)
	assert.DeepEqual(t, statusNodes, aObjNodes.clusterStatusNodes)

	data := afterConfigMap.Data

	if v, ok := data["nodes"]; ok {
		assert.DeepEqual(t, v, nodesToString(aObjNodes.cmSpecNodes))
	}
	if v, ok := data["status"]; ok {
		assert.DeepEqual(t, v, nodesToString(aObjNodes.cmStatusNodes))
	}
	return nil
}

// TestUpdateCombinedBKEClusterNormalFields 测试正常更新任意字段
func TestUpdateCombinedBKEClusterNormalFields(t *testing.T) {
	// 测试更新 spec.ClusterConfig.Cluster.Networking
	t.Run("测试更新 spec.ClusterConfig.Cluster.Networking", TestUpdateAnyFields)
}

func newFakeBkeCluster(bkeConfig *confv1beta1.BKEConfig) {
	if bkeConfig != nil {
		fakeBkeClusterBefore = &bkev1beta1.BKECluster{
			ObjectMeta: bkeClusterObjMeta,
			Spec: confv1beta1.BKEClusterSpec{
				ClusterConfig: bkeConfig,
			},
		}
		return
	}
	fakeBkeClusterBefore = &bkev1beta1.BKECluster{
		ObjectMeta: bkeClusterObjMeta,
		Spec: confv1beta1.BKEClusterSpec{
			ClusterConfig: &confv1beta1.BKEConfig{},
		},
	}
	bkeinit.SetDefaultBKEConfig((*bkeinit.BkeConfig)(fakeBkeClusterBefore.Spec.ClusterConfig))
	fakeConfigMapBefore = &corev1.ConfigMap{
		ObjectMeta: bkeClusterObjMeta,
	}
}

func TestUpdateAnyFields(t *testing.T) {
	var err error
	newFakeBkeCluster(nil)
	fClient := newFakeClient()
	combinedBKECluster, err := GetCombinedBKECluster(ctx, fClient, globalNameSpace, fakeClusterName)
	if err != nil {
		t.Error(err)
		return
	}
	pathchFunc := func(cluster *bkev1beta1.BKECluster) {
		cluster.Spec.ClusterConfig.Nodes = []confv1beta1.Node{
			{
				Hostname: "123",
				IP:       "123",
				Role:     []string{"123"},
			},
		}
		cluster.Status.NodesStatus = confv1beta1.NodesStates{
			{
				Node: confv1beta1.Node{
					Hostname: "123",
					IP:       "123",
					Role:     []string{"123"},
				},
				State: bkev1beta1.NodeUnknown,
			},
		}

		cluster.Spec.ClusterConfig.Cluster.Networking = confv1beta1.Networking{
			PodSubnet:     "123",
			ServiceSubnet: "123",
			DNSDomain:     "123",
		}
		cluster.Spec.ControlPlaneEndpoint = confv1beta1.APIEndpoint{
			Host: "123",
			Port: 123,
		}
		cluster.Spec.ClusterConfig.Cluster.KubernetesVersion = "123"
		cluster.Spec.ClusterConfig.Cluster.ContainerRuntime.Param["data-root"] = "/opt/lib/docker"

		cluster.Spec.ClusterConfig.CustomExtra = map[string]string{
			"test": "test",
		}
		cluster.SetAnnotations(map[string]string{
			"test": "test",
		})
		cluster.SetLabels(map[string]string{
			"test": "test",
		})
		condition.ConditionMark(cluster, bkev1beta1.BKEAgentCondition, confv1beta1.ConditionTrue, "test", "test")
		cluster.Status.Phase = bkev1beta1.ClusterReadyOld
	}

	err = UpdateCombinedBKECluster(ctx, fClient, combinedBKECluster, nil, pathchFunc)

	afterBkeCluster := &bkev1beta1.BKECluster{}
	if err := fClient.Get(ctx, bkeClusterGetKey, afterBkeCluster); err != nil {
		t.Error(err)
		return
	}

	assert.DeepEqual(t, afterBkeCluster.GetAnnotations()["test"], combinedBKECluster.GetAnnotations()["test"])
	assert.DeepEqual(t, afterBkeCluster.GetLabels(), combinedBKECluster.GetLabels())
	assert.DeepEqual(t, afterBkeCluster.Spec.ClusterConfig, combinedBKECluster.Spec.ClusterConfig)
	assert.DeepEqual(t, afterBkeCluster.Spec.ControlPlaneEndpoint, combinedBKECluster.Spec.ControlPlaneEndpoint)
	assert.DeepEqual(t, afterBkeCluster.Status.Conditions, combinedBKECluster.Status.Conditions)
	assert.DeepEqual(t, afterBkeCluster.Status.Phase, combinedBKECluster.Status.Phase)
	assert.DeepEqual(t, afterBkeCluster.Spec.ClusterConfig.Nodes, combinedBKECluster.Spec.ClusterConfig.Nodes)
	assert.DeepEqual(t, afterBkeCluster.Status.NodesStatus, combinedBKECluster.Status.NodesStatus)
	assert.DeepEqual(t, afterBkeCluster.Spec.ClusterConfig.Cluster.ContainerRuntime.Param, combinedBKECluster.Spec.ClusterConfig.Cluster.ContainerRuntime.Param)
}

func TestUpdateSetAnnotaons(t *testing.T) {
	bObjNodes := beforeObjNodes{
		cmSpecNodes:   []confv1beta1.Node{node2, node3},
		cmStatusNodes: nil,

		clusterSpecNodes:   []confv1beta1.Node{node1},
		clusterStatusNodes: nil,

		cClusterSpecNodes:   []confv1beta1.Node{node1, node2, node3},
		cClusterStatusNodes: nil,
	}
	//aObjNodes := afterObjNodes{
	//	cmSpecNodes:   []confv1beta1.Node{node2, node3},
	//	cmStatusNodes: []confv1beta1.Node{node2, node3},
	//
	//	clusterSpecNodes:   []confv1beta1.Node{node1},
	//	clusterStatusNodes: []confv1beta1.Node{node1},
	//}
	operateNodes := []confv1beta1.Node{node1, node2, node3}

	newBeforeObj(bObjNodes)
	var err error
	fClient := newFakeClient()
	combinedBKECluster, err = GetCombinedBKECluster(ctx, fClient, globalNameSpace, fakeClusterName)
	if err != nil {
		t.Error(err)
		return
	}
	assert.DeepEqual(t, combinedBKECluster.Spec.ClusterConfig.Nodes, bObjNodes.cClusterSpecNodes)
	statusNodes := phaseutil.GetBKENodesFromNodesStatus(combinedBKECluster.Status.NodesStatus)
	assert.DeepEqual(t, statusNodes, bObjNodes.cClusterStatusNodes)

	// 模拟加入了两个新节点
	for _, node := range operateNodes {
		combinedBKECluster.Status.NodesStatus = append(combinedBKECluster.Status.NodesStatus, confv1beta1.NodeState{Node: node, State: bkev1beta1.NodeUnknown})
	}

	err = UpdateCombinedBKECluster(ctx, fClient, combinedBKECluster, nil)
	if err != nil {
		return
	}

	combinedBKECluster, err = GetCombinedBKECluster(ctx, fClient, globalNameSpace, fakeClusterName)

	afterCombinedBKECluster := &bkev1beta1.BKECluster{}
	if err := fClient.Get(ctx, bkeClusterGetKey, afterCombinedBKECluster); err != nil {
		t.Error(err)
		return
	}

	v, ok := annotation.HasAnnotation(afterCombinedBKECluster, annotation.LastUpdateConfigurationAnnotationKey)
	if !ok {
		t.Error("not found annotation")
	}
	annotationBkeCluster := &bkev1beta1.BKECluster{}

	err = json.Unmarshal([]byte(v), annotationBkeCluster)
	if err != nil {
		return
	}

	afterCombinedBKECluster = cleanBkeCluster(afterCombinedBKECluster)

	assert.DeepEqual(t, afterCombinedBKECluster.Spec, annotationBkeCluster.Spec)
	assert.DeepEqual(t, afterCombinedBKECluster.Status, annotationBkeCluster.Status)
}
